﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Dexter.Domain.Model;
using Dexter.Logger.Contracts;
using Dexter.PluginManager;
using Dexter.Plugins.Linkflood;
using Dexter.Plugins.Linkflood.Models;
using Dexter.Scheduler;
using Dexter.Services;
using Dexter.Web.Utils;
using Newtonsoft.Json;
using Configuration = Dexter.Plugins.Linkflood.Configuration;

namespace Dexter.Plugins.SelectiveTwitter.Jobs {
	public class TwitterGrabJob : IFrequentlyJob {
		internal const string UNIQUE_ID = "467f2b90-8dea-4d1d-92a1-f34f07ad86ae";
		readonly IWebUtils webUtils;
		readonly IPluginService pluginService;
		readonly ILogger logger;
		readonly Plugin selectiveTweerPlugin;
		readonly Configuration pluginConfiguration = ConfigurationHelper.GetConfiguration;
		readonly HashSet<PluginData> pluginDataCache = new HashSet<PluginData>();
		long lastTweetId;

		public TwitterGrabJob(IWebUtils webUtils, ILogger logger, IPluginService pluginService) {
			this.webUtils = webUtils;
			this.logger = logger;
			this.pluginService = pluginService;

			selectiveTweerPlugin = pluginService.GetPluginByUniqueKey(new Guid(Constants.PLUGIN_IDENTIFIER));

			if(selectiveTweerPlugin == null)
				throw new PluginException(Constants.PLUGIN_IDENTIFIER);

			var lastTweetStored = pluginService.RetrieveDataForSpecifiedDateRange(selectiveTweerPlugin,DateTime.Now.AddYears(-10), DateTime.Now, 1, 0);

			lastTweetId = lastTweetStored == null || !lastTweetStored.Result.Any() ? 1 : lastTweetStored.Result.First().Key.ToInt64();
		}

		#region IFrequentlyJob Members

		/// <summary>
		/// 	Gets a value indicating whether [auto manage session].
		/// </summary>
		/// <value> <c>true</c> if [auto manage session]; otherwise, <c>false</c> . </value>
		public bool AutoManageSession {
			get { return true; }
		}

		/// <summary>
		/// 	Gets the name of the group.
		/// </summary>
		/// <value> The name of the group. </value>
		public string GroupName {
			get { return "SelectiveTwitterPlugin"; }
		}

		/// <summary>
		/// 	Set to <c>false</c> if the state of Job will be reused from the next startup. Otherwise <c>false</c> .
		/// </summary>
		/// <value> <c>true</c> if this instance is volatile; otherwise, <c>false</c> . </value>
		public bool IsVolatile {
			get { return true; }
		}

		/// <summary>
		/// 	This method will be exececuted by the <see cref="IScheduler" />
		/// </summary>
		public void Execute() {
			string twitterUrl = string.Format("https://api.twitter.com/1/statuses/user_timeline.json?include_entities=false&include_rts=false&screen_name={0}&count=20&exclude_replies=true&since_id={1}", pluginConfiguration.Owner, lastTweetId);

			var response = webUtils.DownloadWebPage(new Uri(twitterUrl));

			try {
				dynamic j = JsonConvert.DeserializeObject<dynamic>(response);

				List<Tweet> tweetsToStore = new List<Tweet>();

				foreach (var tweet in j)
				{
					string msg = tweet.text;
					bool storeMsg = false;

					if (pluginConfiguration.GrabCriteria.HashTags.Any(msg.Contains))
					{
						tweetsToStore.Add(ConvertTweet(tweet));
						storeMsg = true;
					}

					if (!storeMsg)
					{
						if (pluginConfiguration.GrabCriteria.Mentions.Any(msg.Contains))
						{
							tweetsToStore.Add(ConvertTweet(tweet));
						}
					}
				}

				if (tweetsToStore.Any())
					lastTweetId = tweetsToStore.First().Id;

				//I've to recerse the order because the last post must have the most recent date.
				tweetsToStore.Reverse();

				foreach (Tweet tweet in tweetsToStore) {
					try {
						string key = tweet.Id.ToString(CultureInfo.InvariantCulture);
						
						PluginData data = pluginDataCache.FirstOrDefault(x => x.Key == key);

						if(data == null) {
							data = pluginService.RetrieveDataFromKey(selectiveTweerPlugin, key);

							if(data != null)
								pluginDataCache.Add(data);
						}

						if(data == null) {
							data = PluginData.CreateNewPluginData(selectiveTweerPlugin, key, tweet.Serialize());
							pluginService.SaveOrUpdateData(data);
						}
					}
					catch (Exception e) {
						logger.Warn(e.Message, e);
					}
				}
			}
			catch (Exception e) {
				//probably twitter is overcapacity
				logger.Warn(e.Message, e);
			}
		}

		/// <summary>
		/// 	Gets the unique id.
		/// </summary>
		/// <value> The unique id. </value>
		public string UniqueId {
			get { return UNIQUE_ID; }
		}

		/// <summary>
		/// 	The elapsed minutes from a job execution and the next one.
		/// </summary>
		/// <value> The minutes. </value>
		public int Minutes {
			get { return pluginConfiguration.FetchMinutesFrequency; }
		}

		#endregion

		Tweet ConvertTweet(dynamic tweet) {
			Tweet t = new Tweet();
			t.Id = tweet.id;
			t.Text = tweet.text;
			t.CreationDate = DateTime.ParseExact((string)tweet.created_at, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture);

			return t;
		}
	}
}